package com.qunar.toku.loadbalance;

import java.util.concurrent.atomic.AtomicInteger;

import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;

import com.qunar.toku.configuration.ServerProfile;
import com.qunar.toku.loadbalance.component.ServerState;
import com.qunar.toku.proxy.Future;
import com.qunar.toku.proxy.RequestProtocol;
import com.qunar.toku.socket.CSocket;
import com.qunar.toku.socket.SocketPool;

public class Server {
	private final InternalLogger logger = InternalLoggerFactory.getInstance(Server.class);
	private String name;
	private String address;
	private int port;
	private float weightRage;
	private ServerState state;
	private SocketPool socketPool;
	private AtomicInteger currUserCount = new AtomicInteger(0);
	private int deadTimeout;
	private long deadTime;

	protected Server(ServerProfile config) {
		this.name = config.getName();
		this.address = config.getHost();
		this.port = config.getPort();
		this.weightRage = config.getWeightRate();
		this.deadTimeout = config.getDeadTimeout();
		if (this.weightRage >= 0) {
			this.state = ServerState.Normal;
		} else {
			this.state = ServerState.Disable;
		}
	}

	public Future request(RequestProtocol protocol) throws Exception {
		if (state == ServerState.Dead) {
			logger.warn("This proxy server is unavailable.state:" + state + "+host:" + address);
			throw new Exception("This proxy server is unavailable.state:" + state + "+host:" + address);
		}

		increaseCU();
		Future future = null;
		CSocket socket = null;

		try {
			future = socketPool.getFuture();
			socket = future.getSocket();
			future.preinvoke(protocol);
			socket.send(protocol);
		} catch (Exception e) {
			logger.error("Server get socket Exception", e);
			if (socket == null || !socket.connecting()) {
				markAsDead();
			}
			throw e;
		} finally {
			decreaseCU();
		}

		return future;
	}
	
	private void markAsDead() throws Exception {
        logger.info("markAsDead server:" + this.state + "--server hashcode:" + this.hashCode() + "--conn count:" + this.socketPool.count());
        if (this.state == ServerState.Dead) {
            logger.info("before markAsDead the server is dead!!!");
            return;
        }
        synchronized (this) {
            if (this.state == ServerState.Dead) {
                logger.info("before markAsDead the server is dead!!!");
                return;
            }
            logger.warn("this server is dead!------------------------------------------------host:" + address);
            this.setState(ServerState.Dead);
            this.deadTime = System.currentTimeMillis();
            this.socketPool.destroy();
        }
    }

	private void increaseCU() {
		currUserCount.incrementAndGet();
	}

	private void decreaseCU() {
		currUserCount.decrementAndGet();
		if (currUserCount.get() <= 0) {
			currUserCount.set(0);
		}
	}
	
	public String getName() {
		return name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public float getWeightRage() {
		return weightRage;
	}

	public ServerState getState() {
		return state;
	}

	public void setState(ServerState state) {
		this.state = state;
	}

	public int getDeadTimeout() {
		return deadTimeout;
	}

	public void setDeadTimeout(int deadTimeout) {
		this.deadTimeout = deadTimeout;
	}

	public long getDeadTime() {
		return deadTime;
	}

	public void setDeadTime(long deadTime) {
		this.deadTime = deadTime;
	}

	public void setSocketPool(SocketPool socketPool) {
		this.socketPool = socketPool;
	}

	public int getCurrUserCount() {
		return currUserCount.get();
	}

	public SocketPool getSocketPool() {
		return socketPool;
	}
	
	 @Override
	    public String toString() {
	        return "Name:" + name + ",Address:" + address + ",Port:" + port + ",State:" + state.toString() + ",CurrUserCount:" + currUserCount + ",ScoketPool:" + socketPool.count();
	    }

}
